package com.globalwave.test;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Table;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hsqldb.util.SqlFile;
import org.hsqldb.util.SqlToolError;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.globalwave.common.Convertor;
import com.globalwave.common.cache.CodeHelper;
import com.globalwave.common.db.SimpleJDBCDAO;
import com.globalwave.common.db.ConnectionProvider;
import com.globalwave.common.service.InitServiceImpl;
import com.globalwave.system.entity.SessionUser;
import com.globalwave.system.entity.User;


@Ignore
public class BaseTestCase {
	
	public Log log = LogFactory.getLog(this.getClass()) ;
	
	protected static ApplicationContext appContext = null ;
	//171845143
	protected ConnectionProvider connectionProvider = null ;
	protected StatelessSession session = null ;
	protected Connection connection = null ;
	protected SimpleJDBCDAO jdbcDao = null ;

	protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd") ;
	protected SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMddHHmmss") ;


	@Rule public TestName name = new TestName();
	
	public static String DEFAULT_DB = null;//"save" ;

	protected StatelessSession getSession(String name) {
		new Convertor();// ConvertUtils.register
		if (name == null || "".equals(name)) {
			name = "sessionFactory" ;
		}
		StatelessSession session = ((SessionFactory)appContext.getBean(name)).openStatelessSession() ;

		//System.err.println("sessoion - " + session);
		return session ;
	}
	
	protected BaseTestCase() {

	}

	@BeforeClass
	public static void initSprintContext() throws Exception {
		appContext = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
		try {
			CodeHelper.setWebConfig("appContext", appContext) ;
			        	
        	InitServiceImpl initService = (InitServiceImpl)appContext.getBean("initService") ;
        	
        	initService.init() ;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	
	@Before
	public void initDataForMethod() throws Exception {
		try {
			connectionProvider = new ConnectionProvider() ;
			session = getSession(DEFAULT_DB) ;
			this.connection = session.connection() ;
			jdbcDao = new SimpleJDBCDAO() ;
			jdbcDao.setConnection(connection) ;
			//connection = connectionProvider.get.getSession().connection() ;
        	
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("\n\n\n ****************** new case(" + name.getMethodName() + ") init data ****************** \n");
		
		//accessor = DBFactory.getDBAccessor("save") ;
		String clz = this.getClass().getSimpleName() ;
		String sqlFile = getSqlFileDir() + clz + ".sql" ;
		execute(sqlFile) ;
		
		sqlFile = getSqlFileDir() + clz + "_" + name.getMethodName() + ".sql" ;
		execute(sqlFile) ;
		
		this.initUser01() ;
	}
	
	@After
	public void runAfterEveryTest() throws Exception {
		//accessor.close() ;
		//accessor = null ;
		session.close() ;
		session = null ;
	}
	
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
	}

	protected void execute(String sqlFile) throws Exception {
		execute(sqlFile, connection) ;
	}
	
	/**
	 * Executes a single sql file
	 * 
	 * @param sqlFile Path of the file to execute(it can be a classpath
	 *        resource)
	 * @throws IOException
	 * @throws SqlToolError
	 * @throws SQLException
	 */
	protected void execute(String sqlFile, Connection con) throws Exception {
		log.debug("Execute " + sqlFile);
		
		System.out.println("Execute " + sqlFile);
		
		URL url = this.getClass().getClassLoader().getResource(sqlFile) ;
		//URL url = getClazzLoader().getResource(sqlFile) ;
		System.out.println("URL " + url);
		if (url == null) {
			System.out.println("path load - " + this.getClass().getClassLoader().getResource("")) ;
			System.out.println(" ========================================================= \n");
			return ;
		}
		
		File file = new File(url.getFile());
		/* 
		if (!file.exists() || !file.canRead()) {
			file = File.createTempFile(file.getName(), ".sql");
			file.deleteOnExit();
			FileUtil.copyResource(sqlFile, file);
		}*/
		if (!file.exists()) {
			System.err.println("file not exists: " + url.getFile());
			throw new  Exception() ;
		}

		SqlFile sFile = new SqlFile(file, false, null);
		try {
			con.setAutoCommit(true) ;
			sFile.execute(con, new Boolean(true));
			System.out.println(" ========================================================= \n");
		} catch (SqlToolError e) {
			e.printStackTrace() ;
			throw new SQLException(e.getMessage());
		}
	}
	
	protected String getSqlFileDir() {

		return this.getClass().getPackage().getName().replace(".", "/") + "/sql/" ;
		
	}


	protected String getSqlFile(Class clz) {
		return getSqlFile(clz, null) ;
	}
	protected String getSqlFile(Class clz, String methodName) {
		if (methodName == null) {
			methodName = "" ;
		} else {
			methodName = "_" + methodName ;
		}
		return getSqlFileDir() + clz.getSimpleName() + methodName + ".sql" ;
		
	}

	protected Object get(Class clz, String[] wheres) throws Exception {
		String hql = buildHql(clz, wheres);

		SimpleJDBCDAO dao = new SimpleJDBCDAO() ;
		dao.setConnection(connection) ;
		List l = (List)dao.query(new StringBuffer(hql), null, null, clz) ;
		
		if (l.size() > 0) {
			return l.get(0) ;
		}
		return null;
		//return get(hql);
	}
	protected Object get(String hql) throws Exception {
		System.out.println(hql);
		return session.createQuery(hql).uniqueResult();
	}

	protected List<Object> query(Class clz, String[] wheres) throws Exception {
		String hql = buildHql(clz, wheres);
		SimpleJDBCDAO dao = new SimpleJDBCDAO() ;
		dao.setConnection(connection) ;
		return (List<Object>)dao.query(new StringBuffer(hql), null, null, clz) ;
		//return query(hql);
	}

	protected List<Object> query(String hql) throws Exception {
		System.out.println(hql);
		return session.createQuery(hql).list();
	}
	
	private String buildHql(Class clz, String[] wheres) {
		Table t = (Table)clz.getAnnotation(Table.class) ;
		
		String hql = "select * from " + t.name() + " p" ;
		hql += " where 1=1 " ;
		for (String c:wheres) {
			hql += " and p." + c ;
		}
		return hql;
	}

	

	protected void printObjectJavaCode(Object obj, String varName) throws Exception {
		System.out.println("\n\n\n");
		
		System.out.println(getJavaNewCode(obj, varName, null)) ;
		
		PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj) ;
		
		for (PropertyDescriptor property:properties) {
			Object value = PropertyUtils.getProperty(obj, property.getName()) ;

			if (value instanceof BigDecimal) {
				value = getJavaNewCode(value, null, value) ;
			}
			
			System.out.println(varName + "." + property.getWriteMethod().getName() + "(" + value + ")") ;
		}
		
		System.out.println("\n\n\n");
	}
	

	protected void printMapJavaCode(Map<String, Object> map, String varName) throws Exception {
		System.out.println("\n\n\n");
		
		System.out.println("Map<String, Object> " + varName + " = new HashMap<String, Object>();") ;
				
		for (String key:map.keySet()) {
			Object value = map.get(key) ;

			if (value instanceof BigDecimal) {
				value = getJavaNewCode(value, null, value) ;
			}
			
			System.out.println(varName + ".put(\"" + key + "\", " + value + ")") ;
		}
		
		System.out.println("\n\n\n");
	}
	
	private String getJavaNewCode(Object obj, String varName, Object value) throws Exception {
		String objName = obj.getClass().getName() ;
		String v = (value == null)?"":String.valueOf(value) ;
		
		if (varName == null) {
		    return objName + " " + varName + " = new " + objName + "(" + v + ");" ;
		} else {
			return " new " + objName + "(" + v + ");" ;
		}
	}
	
	/**
	 * ȡ������
	 * 
	 * @param obj
	 * @param properties
	 * @return
	 * @throws Exception
	 */
	public String[] v(Object obj, String[] properties) throws Exception {
		
		List<String> list = new ArrayList<String>();
		
		PropertyDescriptor[] propertieDescs = PropertyUtils.getPropertyDescriptors(obj) ;
		for (PropertyDescriptor property:propertieDescs) {
			list.add(property.getName()) ;
		}
		
		for (String p: properties) {
			list.remove(p) ;
		}
		
		return list.toArray(new String[]{}) ;
	}
	
	public void assertMap(String msg, Map<String, Object> objExpected, Map<String, Object> objActual, String[] keys) throws Exception {
		if (keys == null) {
			keys = objExpected.keySet().toArray(new String[]{}) ;
		}
		
		for (String property:keys) {
			Object expected = objExpected.get(property) ;
			Object actual = objActual.get(property);
			Assert.assertEquals(msg + " - " + property + " :", expected, actual ) ;
		}
		
	}
	
	public void assertObject(String msg, Object objExpected, Object objActual, String[] properties) throws Exception {
		for (String property:properties) {
			Object expected = PropertyUtils.getProperty(objExpected, property) ;
			Object actual = PropertyUtils.getProperty(objActual, property) ;
			Assert.assertEquals(msg + " - " + property + " :", expected, actual ) ;
		}
		
	}
	
	public Timestamp getDate(String dateString) {
		try {
			return new Timestamp(dateFormat.parse(dateString).getTime()) ;
		} catch (ParseException e) {
			e.printStackTrace() ;
			return null ;
		}
	}
	

	public void execute(Connection connection, String ... sqls) throws Exception {

		Statement s = connection.createStatement() ;
		
		for (String sql:sqls) {
			try {
			    s.execute(sql) ;
			} catch (Exception e) {
				System.err.println("sql can't exe:" + sql);
				log.error("sql can't exe:" + sql, e) ;
			}
		}
		
		s.close() ;
	}
	

	
	public List<Map<String, Object>> select(Connection connection, String table, String ... whereKeyValues) throws Exception {
		List<Map<String, Object>> result = new ArrayList<Map<String,Object>>() ;
		
		String where = " where 1= 1" ;
		for (String kv:whereKeyValues) {
			where += " and " + kv ;
		}
		String sql = "select * from " + table + where ;

		System.out.println("sql :" + sql);
		
		ResultSet rs = null ;
		
		Statement s = connection.createStatement() ;
		try {
			rs = s.executeQuery(sql) ;
		} catch (Exception e) {
			System.err.println("sql can't exe:" + sql);
			log.error("sql can't exe:" + sql, e) ;
		}
		
		if (rs != null) {
			while (rs.next()) {
				
				Map<String, Object> row = new HashMap<String, Object>() ;
				result.add(row) ;
				
				for (int i = rs.getMetaData().getColumnCount() ; i > 0 ; i --) {				
					row.put(rs.getMetaData().getColumnLabel(i).toLowerCase(), rs.getObject(i)) ;
				}
			}
			
			rs.close() ;
		}
		
		s.close() ;
		
		return result ;
	}
	
	
	protected void initUser01() throws Exception {
		SessionUser user = new SessionUser() ;
		user.setUser(new User()) ;
		user.getUser().setShop_code("UTC1") ;
		user.getUser().setRegion_code("360") ;
		user.setSale_date(this.dateFormat.parse("20111108"));
		SessionUser.set(user) ;
	}
}
